
#ifndef _OBCDOCUMENT_H_
#define _OBCDOCUMENT_H_

#include <BMap>
#include <BScene>
#include <BMesh>
#include <BCone>
#include <BCube>
#include <BCylinder>
#include <BSphere>
#include <BGeoSphere>
#include <BTube>
#include <BTorus>
#include <BSurface>
#include <BTerrain>
#include <BTextual>
#include <BBillboard>
#include <BParticle>
#include <BCapsule>
#include <BPointConstraint>
#include <BHingeConstraint>
#include <BSliderConstraint>
#include <BConearConstraint>
#include <BSixdofConstraint>

#include <BImage>
#include <BFont>
#include <BStyle>
#include <BPixmap>
#include <BMaterial>
#include <BProgram>
#include <BUniform>
#include <BPhysics>
#include <BScript>
#include <BSource>
#include <BSpring>
#include <BSpacer>

#include <BApplication>
#include <BFile>
#include <BBuffer>
#include <BReadWrite>

using namespace BWE;

enum SIGN
{
	SIGN_NONE			= 0,	//invalid chunk sign.
	SIGN_MATRIX			= 1,
	SIGN_BOX			= 2,
	SIGN_QUAD			= 3,
	SIGN_VERTICES		= 4,
	SIGN_NORMALS		= 5,
	SIGN_SIZE			= 9,
	SIGN_UP				= 10,
	SIGN_CENTER			= 11,
	SIGN_NEAR			= 12,
	SIGN_FAR			= 13,
	SIGN_FOVY			= 14,
	SIGN_ASPECT			= 15,
	SIGN_AXIS			= 16,
	SIGN_ELEVATION		= 17,
	SIGN_GLYPSIZE		= 18,
	SIGN_LENGTH			= 19,
	SIGN_WIDTH			= 20,
	SIGN_HEIGHT			= 21,
	SIGN_RADIUS			= 22,
	SIGN_THICKNESS		= 23,
	SIGN_INNERRADIUS	= 24,
	SIGN_OUTERRADIUS	= 25,
	SIGN_POSITION		= 26,
	SIGN_LENSSIZE		= 27,
	SIGN_DIRECTION		= 28,
	SIGN_MASS			= 30,
	SIGN_FRICTION		= 31,
	SIGN_ELASTANE		= 32,
	SIGN_FORCE			= 33,
	SIGN_GRAVITY		= 34,
	SIGN_POINTA			= 41,
	SIGN_POINTB			= 42,
	SIGN_ERP			= 43,
	SIGN_CFM			= 44,
	SIGN_TAU			= 45,
	SIGN_DAMPING		= 46,
	SIGN_IMPULSECLAMP	= 47,
	SIGN_BREAK_IMPULSE	= 49,

	SIGN_MATRIX_D		= 50 + SIGN_MATRIX,
	SIGN_BOX_D			= 50 + SIGN_BOX,
	SIGN_QUAD_D			= 50 + SIGN_QUAD,
	SIGN_VERTICES_D		= 50 + SIGN_VERTICES,
	SIGN_NORMALS_D		= 50 + SIGN_NORMALS,
	SIGN_SIZE_D			= 50 + SIGN_SIZE,
	SIGN_UP_D			= 50 + SIGN_UP,
	SIGN_CENTER_D		= 50 + SIGN_CENTER,
	SIGN_NEAR_D			= 50 + SIGN_NEAR,
	SIGN_FAR_D			= 50 + SIGN_FAR,
	SIGN_FOVY_D			= 50 + SIGN_FOVY,
	SIGN_ASPECT_D		= 50 + SIGN_ASPECT,
	SIGN_AXIS_D			= 50 + SIGN_AXIS,
	SIGN_ELEVATION_D	= 50 + SIGN_ELEVATION,
	SIGN_GLYPSIZE_D		= 50 + SIGN_GLYPSIZE,
	SIGN_LENGTH_D		= 50 + SIGN_LENGTH,
	SIGN_WIDTH_D		= 50 + SIGN_WIDTH,
	SIGN_HEIGHT_D		= 50 + SIGN_HEIGHT,
	SIGN_RADIUS_D		= 50 + SIGN_RADIUS,
	SIGN_THICKNESS_D	= 50 + SIGN_THICKNESS,
	SIGN_INNERRADIUS_D	= 50 + SIGN_INNERRADIUS,
	SIGN_OUTERRADIUS_D	= 50 + SIGN_OUTERRADIUS,
	SIGN_POSITION_D		= 50 + SIGN_POSITION,
	SIGN_LENSSIZE_D		= 50 + SIGN_LENSSIZE,
	SIGN_DIRECTION_D	= 50 + SIGN_DIRECTION,
	SIGN_MASS_D			= 50 + SIGN_MASS,
	SIGN_FRICTION_D		= 50 + SIGN_FRICTION,
	SIGN_ELASTANE_D		= 50 + SIGN_ELASTANE,
	SIGN_FORCE_D		= 50 + SIGN_FORCE,
	SIGN_GRAVITY_D		= 50 + SIGN_GRAVITY,
	SIGN_POINTA_D		= 50 + SIGN_POINTA,
	SIGN_POINTB_D		= 50 + SIGN_POINTB,
	SIGN_ERP_D			= 50 + SIGN_ERP,
	SIGN_CFM_D			= 50 + SIGN_CFM,
	SIGN_TAU_D			= 50 + SIGN_TAU,
	SIGN_DAMPING_D		= 50 + SIGN_DAMPING,
	SIGN_IMPULSECLAMP_D	= 50 + SIGN_IMPULSECLAMP,
	SIGN_BREAK_IMPULSE_D	= 50 + SIGN_BREAK_IMPULSE,

	SIGN_ID				= 100,
	SIGN_SCENE			= 101,
	SIGN_NODE			= 102,
	SIGN_LIGHT			= 103,
	SIGN_CAMERA			= 105,
	SIGN_GROUP			= 108,

	SIGN_MESH			= 121,
	SIGN_GEOMETRY		= 122,
	SIGN_ELEMENT		= 123,
	SIGN_CONE			= 126,
	SIGN_CUBE			= 127,
	SIGN_CYLINDER		= 128,
	SIGN_SPHERE			= 129,
	SIGN_GEOSPHERE		= 130,
	SIGN_TORUS			= 131,
	SIGN_TUBE			= 132,
	SIGN_SURFACE		= 133,
	SIGN_TERRAIN		= 134,
	SIGN_BILLBOARD		= 135,
	SIGN_PARTICLE		= 136,
	SIGN_TEXTUAL		= 137,
	SIGN_CAPSULE		= 138,

	SIGN_CONSTRAINT		= 140,
	SIGN_POINT_CONSTRAINT	= 141,
	SIGN_HINGE_CONSTRAINT	= 142,
	SIGN_SLIDER_CONSTRAINT	= 143,
	SIGN_CONEAR_CONSTRAINT	= 144,
	SIGN_SIXDOF_CONSTRAINT	= 145,

	SIGN_INNER_COLLIBLE		= 151,
	SIGN_NODEA				= 152,
	SIGN_NODEB				= 153,

	SIGN_FONT			= 200,
	SIGN_STYLE			= 201,
	SIGN_IMAGE			= 208,
	SIGN_PIXMAP			= 209,

	SIGN_MATERIAL		= 225,
	SIGN_TEXTURE		= 227,
	SIGN_PROGRAM		= 229,
	SIGN_SHADER			= 230,
	SIGN_UNIFORM		= 231,
	SIGN_PHYSICS		= 232,
	SIGN_SCRIPT			= 236,
	SIGN_SOURCE			= 237,
	SIGN_EXP			= 238,

	SIGN_WIDGET			= 240,
	SIGN_CHILDREN		= 241,

	SIGN_OBJECT			= 300,
	SIGN_NAME			= 301,
	SIGN_ENABLED		= 302,
	SIGN_VISIBLE		= 304,
	SIGN_OPTIMIZED		= 305,
	SIGN_RECT			= 307,
	SIGN_VALUE			= 308,

	SIGN_SHAPE			= 309,
	SIGN_LIGHTING		= 310,
	SIGN_DEPTHTEST		= 311,
	SIGN_CULLBACK		= 312,
	SIGN_LINESMOOTH		= 314,
	SIGN_LINEWIDTH		= 315,
	SIGN_POINTSMOOTH	= 316,
	SIGN_POINTSIZE		= 317,
	SIGN_POLYGONSMOOTH	= 318,
	SIGN_POLYGONMODE	= 319,
	SIGN_TRANSPARENT	= 320,

	SIGN_COLOR			= 330,
	SIGN_COLORS			= 331,
	SIGN_COLLIDING		= 332,
	SIGN_SIDES			= 333,
	SIGN_SEQUENCE		= 334,
	SIGN_SYNTAX			= 335,
	SIGN_INFORMATION	= 336,

	SIGN_AMBIENT		= 341,
	SIGN_DIFFUSE		= 342,
	SIGN_SPECULAR		= 343,
	SIGN_EMISSION		= 344,
	SIGN_SHININESS		= 345,
	SIGN_ATTENUATION	= 346,
	SIGN_ATTENUATION2	= 347,
	SIGN_CUTOFF			= 348,
	SIGN_EXPONENT		= 349,

	SIGN_TEXCOORDS		= 362,
	SIGN_FACES			= 363,
	SIGN_ORTHO			= 365,
	SIGN_PIXELSIZE		= 376,
	SIGN_PIXELPOLICY	= 377,
	SIGN_BACKGROUND		= 378,
	SIGN_FILENAME		= 379,
	SIGN_FORMAT			= 380,
	SIGN_PIXELS			= 382,
	SIGN_ORIGIN			= 399,
	SIGN_RIGHT			= 400,
	SIGN_FRONT			= 401,
	SIGN_ALIGN			= 404,
	SIGN_FLAGS			= 405,
	SIGN_SEGMENTS		= 410,
	SIGN_HORSEGMENT		= 411,
	SIGN_VERSEGMENT		= 412,
	SIGN_RADIUSSEGMENTS = 413,
	SIGN_LENGTHSEGMENTS = 414,
	SIGN_WIDTHSEGMENT	= 415,
	SIGN_HEIGHTSEGMENT	= 416,

	SIGN_BLEND			= 451,
	SIGN_FILTER			= 452,
	SIGN_WRAP			= 453,
	SIGN_BLENDCOLOR		= 454,
	SIGN_MIPMAP			= 460,
	SIGN_PRIMITIVE		= 461,
	SIGN_FIRST			= 462,
	SIGN_COUNT			= 463,
	SIGN_INDICES		= 464,
	SIGN_PIECES			= 466,
	SIGN_TYPE			= 467,
	SIGN_TEXT			= 468,
	SIGN_SIGN			= 469,

	SIGN_MAX			= 500,
};

#pragma pack(1)
struct Chunk
{
	Chunk()
	{
		sign = 0;
		end = 0;
	}
	unsigned short	sign;
	unsigned int	end;
};
#pragma pack()

class ObcDocument
{
public:
	ObcDocument();
	~ObcDocument();

	BObject* load(const BString& fileName);
	bool load(const BString& fileName, BObject* object);
	bool save(const BString& fileName, const BObject* object);

	BObject* read(const BStream& stream);
	bool read(const BStream& stream, BObject* object);
	bool write(BStream& stream, const BObject* object);

	inline BString& message()
	{
		if (_message.empty())
			_message = new BString();
		BString& text = _message;
		return text;
	}
	inline const BString& message() const
	{
		const BString& text = _message;
		return text;
	}

protected:
	void reset();
	BObject* createObject(const Chunk& chunk);

	BObject* read(const Chunk& chunk);

	//read chunk
	bool readBeginChunk(Chunk& chunk, int end);
	bool readEndChunk(Chunk& chunk);

	bool readObject(const Chunk& chunk, BObject* object);
	bool readScene(const Chunk& chunk, BScene* scene);
	bool readGroup(const Chunk& chunk, BGroup* group);
	bool readNode(const Chunk& chunk, BNode* node);
	bool readLight(const Chunk& chunk, BLight* light);
	bool readCamera(const Chunk& chunk, BCamera* camera);
	bool readBillboard(const Chunk& chunk, BBillboard* billboard);
	bool readConstraint(const Chunk& chunk, BConstraint* constraint);
	bool readPointConstraint(const Chunk& chunk, BPointConstraint* constraint);
	bool readHingeConstraint(const Chunk& chunk, BHingeConstraint* constraint);
	bool readSliderConstraint(const Chunk& chunk, BSliderConstraint* constraint);
	bool readConearConstraint(const Chunk& chunk, BConearConstraint* constraint);
	bool readSixdofConstraint(const Chunk& chunk, BSixdofConstraint* constraint);

	bool readShape(const Chunk& chunk, BShape* shape);
	bool readMesh(const Chunk& chunk, BMesh* mesh);
	bool readGeometry(const Chunk& chunk, BGeometry* geometry);
	bool readTextual(const Chunk& chunk, BTextual* textual);
	bool readParticle(const Chunk& chunk, BParticle* particle);
	bool readElement(const Chunk& chunk, BElement* element);

	bool readCube(const Chunk& chunk, BCube* cube);
	bool readCone(const Chunk& chunk, BCone* cone);
	bool readCylinder(const Chunk& chunk, BCylinder* cylinder);
	bool readSphere(const Chunk& chunk, BSphere* sphere);
	bool readGeoSphere(const Chunk& chunk, BGeoSphere* geoSphere);
	bool readTube(const Chunk& chunk, BTube* tube);
	bool readTorus(const Chunk& chunk, BTorus* torus);
	bool readSurface(const Chunk& chunk, BSurface* surface);
	bool readTerrain(const Chunk& chunk, BTerrain* terrain);
	bool readCapsule(const Chunk& chunk, BCapsule* capsule);

	bool readMaterial(const Chunk& chunk, BMaterial* material);
	bool readTexture(const Chunk& chunk, BTexture* texture);
	bool readImage(const Chunk& chunk, BImage* image);
	bool readPixmap(const Chunk& chunk, BPixmap* pixmap);
	bool readProgram(const Chunk& chunk, BProgram* program);
	bool readShader(const Chunk& chunk, BShader* shader);
	bool readUniform(const Chunk& chunk, BUniform* uniform);
	bool readPhysics(const Chunk& chunk, BPhysics* physics);
	bool readScript(const Chunk& chunk, BScript* script);
	bool readSource(const Chunk& chunk, BSource* source);
	bool readExp(const Chunk& chunk, BExp* exp);
	//write chunk.

	bool write(const BObject* object);

	bool writeWidgetData(const BWidget* widget);

	bool writeObject(const BObject* object);
	bool writeScene(const BScene* scene);
	bool writeGroup(const BGroup* group);
	bool writeNode(const BNode* node);
	bool writeLight(const BLight* light);
	bool writeCamera(const BCamera* camera);
	bool writeBillboard(const BBillboard* billboard);
	bool writeConstraint(const BConstraint* constraint);
	bool writePointConstraint(const BPointConstraint* pointConstraint);
	bool writeHingeConstraint(const BHingeConstraint* hingeConstraint);
	bool writeSliderConstraint(const BSliderConstraint* sliderConstraint);
	bool writeConearConstraint(const BConearConstraint* conearConstraint);
	bool writeSixdofConstraint(const BSixdofConstraint* sixdofConstraint);

	bool writeShape(const BShape* shape);
	bool writeMesh(const BMesh* mesh);
	bool writeGeometry(const BGeometry* geometry);
	bool writeTextual(const BTextual* textual);
	bool writeParticle(const BParticle* particle);
	bool writeElement(const BElement* element);

	bool writeCube(const BCube* cube);
	bool writeCone(const BCone* cone);
	bool writeCylinder(const BCylinder* cylinder);
	bool writeSphere(const BSphere* sphere);
	bool writeGeoSphere(const BGeoSphere* geoSphere);
	bool writeTube(const BTube* tube);
	bool writeTorus(const BTorus* torus);
	bool writeSurface(const BSurface* surface);
	bool writeTerrain(const BTerrain* terrain);
	bool writeCapsule(const BCapsule* capsule);

	bool writeMaterial(const BMaterial* material);
	bool writeTexture(const BTexture* texture);
	bool writeImage(const BImage* image);
	bool writePixmap(const BPixmap* pixmap);
	bool writeProgram(const BProgram* program);
	bool writeShader(const BShader* shader);
	bool writeUniform(const BUniform* uniform);
	bool writePhysics(const BPhysics* physics);
	bool writeScript(const BScript* script);
	bool writeSource(const BSource* source);
	bool writeExp(const BExp* exp);

private:
	typedef bool (ObcDocument::* readFunc)(const Chunk&, BObject*);
	typedef bool (ObcDocument::* writeFunc)(const BObject*);
	BMap<BString, readFunc>			_readFuncMap;
	BMap<BString, writeFunc>		_writeFuncMap;
	BMap<BString, SIGN>				_typeSignMap;
	BMap<SIGN, BString>				_signTypeMap;
	BArray<const char*>				_signNames;

	BObjectHolder				_object;
	int							_chunkLevel;
	BStream*					_stream;
	BValue						_message;
	BMap<int, BObject*>			_readObjectMap;
	BMap<const BObject*, int>	_writeObjectMap;

private:
	inline void regist(const BString& type, SIGN sign, const char* signName, readFunc rf, writeFunc wf)
	{
		_readFuncMap[type] = rf;
		_writeFuncMap[type] = wf;
		_typeSignMap[type] = sign;
		_signTypeMap[sign] = type;
		_signNames[sign] = signName;
	}
	friend class AutoChunk;
};

class AutoChunk
{
public:
	AutoChunk(ObcDocument* obcDoc, SIGN chunkSign)
	{
		doc = obcDoc;
		sign = chunkSign;
#ifdef REAL_IS_DOUBLE
		if (sign < 50)
			sign += 50;
#endif
		end = 0;
		begin = doc->_stream->offset();
		doc->_stream->write(sign);
		doc->_stream->write(end);
		doc->_chunkLevel++;
	}
	~AutoChunk()
	{
		end = doc->_stream->offset();
		doc->_stream->seek(begin);
		doc->_stream->write(sign);
		doc->_stream->write(end);
		doc->_stream->seek(end);
		doc->_chunkLevel--;
	}
	unsigned int	begin;
	unsigned short	sign;
	unsigned int	end;
	ObcDocument*	doc;
};

#endif